package service

import (
	"commerce/common"
	"commerce/data"
	"commerce/model"
	"commerce/req"
	"commerce/vo"
	"context"
	"fmt"
	"sync"
	"time"
)

var workPool = sync.Pool{
	New: func() interface{} {
		return func(orderId int, fn func(int)) {
			fn(orderId)
		}
	},
}

func init() {
	workPool.Put(workPool.New())
	workPool.Put(workPool.New())
	workPool.Put(workPool.New())
}

func SaveOrder(dto *req.OrderDTO) (int, error) {

	fail := func(err error) (int, error) {
		return 0, fmt.Errorf("SaveOrder: %v", err)
	}
	ctx := context.Background()
	var err error
	tx, err := common.DB.BeginTx(ctx, nil)
	if err != nil {
		return fail(err)
	}
	/**
		Defer the transaction’s rollback. If the transaction succeeds,
	it will be committed before the function exits, making the deferred rollback call a no-op.
		If the transaction fails it won’t be committed,
	meaning that the rollback will be called as the function exits.
	*/
	defer tx.Rollback()

	id, err := data.AddOrder(tx, dto.Order)
	if err != nil {
		return fail(err)
	}
	err = data.BatchAddOrderItem(tx, dto.OrderItems, id)
	if err != nil {
		return fail(err)
	}
	// 插入订单状态表数据
	err = data.AddOrderStatus(tx, id, common.ORDER_STATE_CREATED)
	if err != nil {
		return fail(err)
	}
	// 远程 OR local 扣减库存(如果是远程调用，则LockOrderStock本身需要是事务性的)
	err = data.LockOrderStock(tx, dto.OrderItems, id, dto.OrderNo)
	if err != nil {
		return fail(err)
	}

	// mq 推送消息：以备用户不付款，通过延迟队列自动关单：取消订单 @ todo 先用golang 定时器实现
	setScheduledOrderTask(id)

	// 成功，提交事务
	if err = tx.Commit(); err != nil {
		return fail(err)
	}
	return id, nil
}

func ListMyOrder(memberId, pageNo, pageSize int) (*common.Page, error) {

	page, err := data.MyOrders(memberId, pageNo, pageSize)
	if err != nil {
		common.Error.Println("err ListMyOrder,err:", err)
		return nil, err
	}
	var orderList []vo.MyOrderVo
	for _, o := range page.List.([]model.Order) {
		mo := vo.MyOrderVo{Id: o.Id, OrderNo: o.OrderNo, OrderState: o.StateStr, PayAmount: o.PayAmount, UnPay: o.State == common.ORDER_STATE_CREATED, Comment: o.State == common.ORDER_STATE_RECEIVED}
		var goodsList []vo.OrderGoodsVo
		for _, i := range o.OrderItemList {
			goodsList = append(goodsList, vo.OrderGoodsVo{Id: i.Id, SkuImg: i.SkuImg, SkuName: i.SkuName, Count: i.Count})
		}
		mo.GoodsList = goodsList

		orderList = append(orderList, mo)
	}
	page.List = orderList

	return page, nil
}

func AppOrderDetail(orderId int, memberId int) (*vo.OrderDetailVo, error) {

	o, err := data.OrderDetail(orderId, memberId)
	if err != nil {
		return nil, err
	}
	var res vo.OrderDetailVo

	res.OrderInfo = vo.OrderDetail{Id: o.Id, OrderNo: o.OrderNo, OrderState: o.State, OrderStateStr: o.StateStr, PayAmount: o.PayAmount, CreatedTime: o.CreatedTimeStr,
		Consignee: o.ReceiverUserName, Mobile: o.ReceiverPhone, FullRegion: fmt.Sprintf("%s/%s/%s", o.ReceiverProvince, o.ReceiverCity, o.ReceiverRegion),
		Address: o.ReceiverDetailAddr, TotalAmount: o.TotalAmount, FreightAmount: o.FreightAmount}

	res.HandleOption = vo.PayOptionVo{Pay: o.State == common.ORDER_STATE_CREATED, Confirm: o.State > common.ORDER_STATE_CREATED && o.State < common.ORDER_STATE_RECEIVED}

	var goodsList []vo.OrderGoodsVo
	for _, i := range o.OrderItemList {
		goodsList = append(goodsList, vo.OrderGoodsVo{Id: i.Id, SkuId: i.SkuId, SkuImg: i.SkuImg, SkuName: i.SkuName, Count: i.Count,
			SpecAttrValues: i.SkuAttrValue, RetailPrice: i.SkuPrice, CanComment: i.Commented == 0 && o.State == common.ORDER_STATE_RECEIVED})
	}
	res.OrderGoods = goodsList

	return &res, nil
}

func CancelOrder(orderId int, memberId int) error {

	return data.UpdateUserOrderStatus(memberId, orderId, common.ORDER_STATE_CANCELLED)
}

func ConfirmOrderGoods(orderId int, memberId int) error {

	order, err := data.ListOrderStatusByIdAndUserId(orderId, memberId)
	if err != nil {
		return err
	}
	if order.State > common.ORDER_STATE_DELIVERING {
		common.Error.Println("ConfirmOrderGoods illegal order state:", order.State)
		return fmt.Errorf("不符合条件")
	}
	return data.UpdateUserOrderStatus(memberId, orderId, common.ORDER_STATE_RECEIVED)
}

func GetOrderDetail(orderId int) (vo.OrderDetailPageVo, error) {

	var wg sync.WaitGroup
	wg.Add(3)

	var res vo.OrderDetailPageVo

	// 订单 & 用户
	go func() {
		defer wg.Done()
		order, err := data.GetOrderDetailById(orderId)
		if err != nil {
			common.Error.Println("1.GetOrderDetail err:", err)
			return
		}
		res.Order = *order

		var member *model.Member
		member, err = data.GetMemberById(order.UserId)
		if err != nil {
			common.Error.Println("2.GetOrderDetail err:", err)
			return
		}
		res.Member = *member
	}()
	// 订单项列表 & 商品信息
	go func() {
		defer wg.Done()
		orderItems, err := data.ListOrderItemByOrderId(orderId)
		if err != nil {
			common.Error.Println("2.GetOrderDetail err:", err)
			return
		}
		res.OrderItems = orderItems

		var skuList []model.Sku
		var sku *model.Sku
		var goods *model.Goods
		for _, i := range orderItems {
			sku, err = data.GetSkuInfoById(i.SkuId)
			if err != nil {
				common.Error.Println("3.GetOrderDetail err:", err)
				continue
			}
			goods, err = data.GetGoodsAndBCById(sku.GoodsId)
			if err != nil {
				common.Error.Println("4.GetOrderDetail err:", err)
				continue
			}
			sku.GoodsName = goods.Name
			sku.GoodsImg = goods.MainImg
			sku.GoodsDesc = goods.Description

			sku.BrandId = goods.BrandId
			sku.BrandName = goods.BrandName
			sku.Logo = goods.Logo
			sku.BrandDesc = goods.BrandDesc

			sku.CategoryId = goods.CategoryId
			sku.CategoryName = goods.CategoryName
			sku.CMainImg = goods.CMainImg
			sku.CDesc = goods.CDesc

			skuList = append(skuList, *sku)
		}
		res.SkuList = skuList
	}()

	go func() {
		defer wg.Done()
		orderStatusList, err := data.ListOrderStatusByOrderId(orderId)
		if err != nil {
			common.Error.Println("5.GetOrderDetail err:", err)
			return
		}
		res.OrderStatusList = orderStatusList
	}()

	wg.Wait()

	return res, nil
}

func setScheduledOrderTask(orderId int) {

	workPool.Get().(func(id int, fn func(int)))(orderId, func(orderId int) {
		// 这是类似于异步的
		time.AfterFunc(common.WAIT_PAY_TIME*time.Minute, func() {
			// 查库，看是否order已经付款了，如果没有，那就更新订单状态为 已取消， 并告之库存：解锁库存
			common.Info.Println("check cancel order: orderId->", orderId)
			order, err := data.GetOrderById(orderId)
			if err != nil {
				common.Error.Printf("check cancel order(%d) err:%v", orderId, err)
				return
			}
			// 用户没付款，（主动取消 会显示地更改订单状态 & 解锁库存， 它不在这里处理）
			if order.State == common.ORDER_STATE_CREATED {
				// 主动调用微信接口，对此订单关闭支付接口@todo

				closeOrder(order)
			}
		})
	})
}

// 关单，等了用户好久，都不付钱

func closeOrder(order *model.Order) error {

	err := data.UpdateOrderStatus(order.Id, common.ORDER_STATE_CANCELLED)
	if err != nil {
		return err
	}
	err = data.AddOrderStatus2(order.Id, common.ORDER_STATE_CANCELLED)
	if err != nil {
		common.Error.Printf("err AddOrderStatus2, orderId:%d, err:%v", order.Id, err)
	}
	/**
	 * 正常的逻辑是：先有此处的自动关单，再有自动解锁库存，但是如果前者路途卡顿、等原因，失败了，
	 * 让自动 解锁库存调用 走在了前面，发现订单还是待付款状态，就不会去解锁库存操作了（自动解锁只针对
	 * 由于事务回滚而不存在的订单 和 已取消状态的订单），所以下面还需要一个主动补偿机制：这里订单关单后
	 * 主动去解锁库存.
	 * 另外，通过状态来保证幂等性，所以就算一切正常：先关单，然后主动去触发解锁库存，之后库存mq/定时器任务 再次去解锁库存时
	 * 也没关系
	 */
	// 只处理还在 进行中 的任务单
	task, err := data.GetWareOrderTaskByOrderId(order.Id, 1)
	if err == nil && task != nil {
		var details []model.WareOrderTaskDetail
		// 找出此任务id下的还未解锁的任务明细记录
		details, err = data.ListWareOrderTaskDetailByTaskId(task.Id, 1)

		if err == nil && details != nil && len(details) > 0 {

			for _, d := range details {
				releaseStock(d.Id, d.SkuId, d.SkuNum)
			}
		}
		// 更新任务表
		data.UpdateWareOrderTaskStatus(task.Id, 2)
	}
	return nil
}

func releaseStock(taskDetailId int, skuId int, skuNum int) {

	data.ReleaseSkuStock(skuNum, skuId)
	//_, err := data.ReleaseSkuStock(skuNum, skuId)
	//if err != nil {
	//	return err
	//}
	data.UpdateWareOrderTaskDetailStatus(taskDetailId, 2)
	//if err != nil {
	//	return err
	//}
}
